爬取国内创业公司、投资机构、独角兽公司信息
摘要: 之前爬的网站都是不需要登录就可以爬取的,但还有很多网站的内容需要先登录才能爬,比如桔子网、豆瓣、知乎等。这时采用之前的方法就不行了,需要先登录再去爬。本文以桔子网为例,介绍模拟登录方法,然后爬取该网站数据库中的数据信息,并保存到 MongoDB 数据库中。
1. 网站介绍
网址:https://www.itjuzi.com/
对于创投圈的人来说,国内的桔子
和国外的 Crunchbase
应该算是必备网站。今天,我们要说的就是前者,这个网站提供了很多有价值的信息。
信息的价值体现在哪里呢,举个简单的例子。你运营了一个不错的公众号,有着 10 万粉丝群,这时候你想找投资继续做大,但你不知道该到哪里去找投资。这时候你偶然发现了这个网站,在网站上你看到了同领域的大 V 号信息,他们得到了好几家公司上千万的投资。你看着心生羡慕,也跃跃欲试。于是,你经过综合对比分析,对自己公众号估值 200 万,然后就去找投资大 V 号的那几家公司洽谈。由于目的性明确,准备也充分,你很快就得到了融资。
这个例子中,桔子网提供了创业公司(运营公众号也是创业)融资金额和投资机构等相关宝贵的信息。当然,这只是非常小的一点价值,该网站数据库提供了自 2000 年以来的海量数据,包括:超过11 万家的创业公司、6 万多条投融资信息、7 千多家投资机构以及其他数据,可以说是非常全了。这些数据的背后蕴含着大量有价值的信息。
接下来,本文尝试爬取该网站数据库的信息,之后做一些数据分析,尝试找到一些有意思的信息。
主要内容:
模拟登录
分析 Ajax 然后抓取
存储到 MongoDB 数据库
导出 csv
数据分析(后期)
2. 模拟登录
2.1. Session 和 Cookies
观察这个网站,是需要先登录才能看到数据信息的,但是好在不用输验证码。我们需要利用账号和密码,然后实现模拟登录。
模拟登录的方法有好几种,比如 Post 直接提交账号、先登录获取 Cookies 再直接请求、Selenium 模拟登录等。其中:
Post 方法需要在后台获取登录的 url,填写表单参数然后再请求,比较麻烦;
直接复制 Cookies 的方法就是先登录账号,复制出 Cookies 并添加到 Headers 中,再用 requests.get 方法提交请求,这种方法最为方便;
Selenium 模拟登录方法是直接输入账号、密码,也比较方便,但速度会有点慢。
之后,会单独介绍几种方法的具体实现的步骤。这里,我们就先采用第二种方法。
首先,需要先了解两个知识点:Session 和 Cookies。
Session 在服务端,也就是网站的服务器,用来保存用户的会话信息,Cookies 在客户端,也可以理解为浏览器端,有了 Cookies,浏览器在下次访问网页时会自动附带上它发送给服务器,服务器通过识别 Cookies 并鉴定出是哪个用户,然后再判断用户是否是登录状态,然后返回对应的 Response。所以我们可以理解为 Cookies 里面保存了登录的凭证,有了它我们只需要在下次请求携带 Cookies 发送 Request 而不必重新输入用户名、密码等信息重新登录了。
因此在爬虫中,有时候处理需要登录才能访问的页面时,我们一般会直接将登录成功后获取的 Cookies 放在 Request Headers 里面直接请求。
更多知识,可以参考崔庆才大神的文章:
https://germey.gitbooks.io/python3webspider/content/2.4-Session%E5%92%8CCookies.html
在了解 Cookies 知识后,我们就可以进入正题了。
2.2. Requests 请求登录
首先,利用已有的账号和密码,登录进网站主页,然后右键-检查,打开第一个 www.itjuzi.com
请求:
向下拉到 Requests Headers 选项,可以看到有很多字段信息,这些信息之后我们都要添加到 Get 请求中去。
定位到下方的 Cookie 字段,可以看到有很多 Cookie 值和名称,这是在登录后自动产生的。我们将整个 Cookies 复制 Request Headers 里,然后请求网页就可以顺利登陆然后爬取。如果不加 Cookies,那么就卡在登录界面,也就无法进行后面的爬取,所以 Cookies 很重要,需要把它放到 Request Headers 里去。
下面,我们按照 json 格式开始构造 Request Headers。这里推荐一个好用的网站,可以帮我们自动构造 Request Headers:https://curl.trillworks.com/
使用方法也很简单,右键复制cURL链接
到这个网页中。
将 cURL 复制到左边选框,默认选择语言为 Python,然后右侧就会自动构造后 requests 请求,包括 headers,复制下来直接可以用。登录好以后,我们就转到投融资速递网页中(url:http://radar.itjuzi.com/investevent),然后就可以获取该页面网页内容了。代码如下:
1import requests
2headers = {
3 'Connection': 'keep-alive',
4 'Cache-Control': 'max-age=0',
5 'Upgrade-Insecure-Requests': '1',
6 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36',
7 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
8 'DNT': '1',
9 'Referer': 'http://radar.itjuzi.com/investevent',
10 'Accept-Encoding': 'gzip, deflate, br',
11 'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8,zh-TW;q=0.7',
12 'If-None-Match': 'W/^\\^5bc7df15-19cdc^\\^',
13 'If-Modified-Since': 'Thu, 18 Oct 2018 01:17:09 GMT',
14 # 主页cookie
15 'Cookie': '你的cookie',
16 }
17
18url = 'http://radar.itjuzi.com/investevent' # 投融资信息
19s = requests.Session()
20response = s.get(url,headers = headers)
21print(response.status_code)
22print(response.text)
可以看到,添加 Cookie 后,我们请求投融资信息网页就成功了。这里如果不加 Cookie 的结果就什么也得不到:
好,这样就算成功登录了。但是整个 headers 请求头的参数太多,是否一定需要带这么多参数呢? 这里就去尝试看哪些参数是请求一定要的,哪些则是不用的,不用的可以去掉以精简代码。经过尝试,仅需要下面三个参数就能请求成功。
1headers = {
2 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36'
3 'X-Requested-With': 'XMLHttpRequest',
4 # 主页cookie
5 'Cookie': '复制你的cookie',
6 }
Tips:当爬取失效的时候需要重新注册帐号,然后生成新的 Cookie。
如果你没那么多邮箱账号,那么推荐一个可生成随机账号的免费邮箱,用来接收注册激活链接:
https://10minutemail.net/
3. 网页爬取分析
在成功登录以后,我们就可以通过分析网页结构来采取相应的爬取方法。这里,我们将爬取投融资速递、创业公司、投资机构和千里马等几个子板块的数据。首先,以投融资速递信息为例。
网址:http://radar.itjuzi.com/investevent
3.1. 分析网页
可以看到,投融资事件信息网页中的数据是表格形式。经尝试点击翻页,发现url不变,可以初步判定网页数据采用 Ajax 形式呈现。切换到后台,点击翻页可以发现出现有规律的 info?locatiop 开头的请求
,页数随 page 参数而发生规律的变化。
点击请求查分别查看 Response 和 Preview,发现表格数据是 json 格式,这就太好了。因为 json 格式的数据非常整齐也很容易抓取。上一篇文章,我们正好解决了 json 格式数据的处理方法,如果不太熟悉可以回顾一下:
https://www.makcyun.top/web_scraping_withpython6.html
接着,我们就可以通过构造 url 参数,然后用 Get 请求就可以获取网页中的表格数据,最后再加个循环,就可以爬取多页数据了。
3.2. 构造 url
下面,我们来构造一下 url,切换到 Headers 选项卡,拉到最底部可以看到 url 需带的请求参数。这里有三项,很好理解。location:in,表示国内数据; orderby:def,表示默认排序;page:1,表示第一页。所以,只需要改变 page 参数就可以查看其他页的结果,非常简单。
这里,如果我们对表格进行筛选,比如行业选择教育、时间选择 2018 年,那么相应的请求参数也会增加。通过构造参数就可以爬取指定的数据,这样就不用全部爬下来了,也对网站友好点。
3.3. 爬取数据
到这儿,我们就可以直接开始爬了。可以使用函数,也可以用定义类(Class)的方法。考虑到,Python 是一种面向对象的编程,类(Class)是面向对象最重要的概念之一,运用类的思想编程非常重要。所以,这里我们尝试采用类的方法来实现爬虫。
1import requests
2import pymongo
3import random
4import time
5import json
6import pandas as pd
7from fake_useragent import UserAgent
8ua = UserAgent()
9
10class ITjuzi(object):
11 def __init__(self):
12 self.headers = {
13 'User-Agent': ua.random,
14 'X-Requested-With': 'XMLHttpRequest',
15 'Cookie': '你的cookie',
16 }
17 self.url = 'http://radar.itjuzi.com/investevent/info?' # investevent
18 self.session = requests.Session()
19
20 def get_table(self, page):
21 """
22 1 获取投融资事件数据
23 """
24 params = { # invsestevent
25 'location': 'in',
26 'orderby': 'def',
27 'page': page,
28 'date': 2018 # 年份
29 }
30 response = self.session.get(
31 self.url, params=params, headers=self.headers).json()
32 print(response)
33 # self.save_to_mongo(response)
34
35if __name__ == '__main__':
36 spider = itjuzi()
37 spider.get_table(1)
如果你之前一直是用 Def 函数的写法,而没有接触过 Class 类的写法,可能会看地比较别扭,我之前就是这样的,搞不懂为什么要有 self
、为什么用__init__
。这种思维的转变可以通过看教程和别人写的实际案例去揣摩。这里,我先略过,之后会单独介绍。
简单解释一下上面代码的意思。首先定义了一个类(class),类名是 ITjuzi,类名通常是大写开头的单词。后面的 (object) 表示该类是从哪个类继承下来的,这个可以暂时不用管,填上就可以。然后定义了一个特殊的__init__
方法,__init__
方法的第一个参数永远是 self
,之后是其他想要设置的属性参数。在这个方法里可以绑定些确定而且必须的属性,比如 headers、url 等。
在 headers 里面,User-Agent 没有使用固定的 UA,而是借助 fake_useragent包
生成随机的 UA:ua.random
。因为,这个网站做了一定的反爬措施,这样可以起到一定的反爬效果,后续我们会再说到。接着,定义了一个 get_table() 函数,这个函数和普通函数没有什么区别,除了第一个参数永远是实例变量 self
。在 session.get()方法中传入 url、请求参数和 headers,请求网页并指定获取的数据类型为 json 格式,然后就可以顺利输出 2018 年投融资信息的第 1 页数据:
3.4. 存储到 MongoDB
数据爬取下来了,那么我们放到哪里呢?可以选择存储到 csv 中,但 json 数据中存在多层嵌套,csv 不能够直观展现。这里,我们可以尝试之前没有用过的 MongoDB 数据库,当作练习。另外,数据存储到该数据库中,后期也可以导出 csv,一举两得。
关于 MongoDB 的安装与基本使用,可以参考下面这两篇教程,之后我也会单独再写一下:
安装
https://germey.gitbooks.io/python3webspider/content/1.4.2-MongoDB%E7%9A%84%E5%AE%89%E8%A3%85.html
使用
https://germey.gitbooks.io/python3webspider/content/5.3.1-MongoDB%E5%AD%98%E5%82%A8.html
可视化工具可采用 Robo 3T (之前叫 RoboMongo)
https://www.mongodb.com/
下面我们就将上面返回的 json 数据,存储到 MongoDB 中去:
1import pymongo
2import numpy as np
3# mongodb数据库初始化
4client = pymongo.MongoClient('localhost', 27017)
5# 指定数据库
6db = client.ITjuzi
7# 指定集合,类似于mysql中的表
8mongo_collection1 = db.itjuzi_investevent
9
10def save_to_mongo(self, response):
11 try:
12 data = response['data']['rows'] # dict可以连续选取字典层内的内容
13 df = pd.DataFrame(data)
14 table = json.loads(df.T.to_json()).values()
15 if mongo_collection1.insert_many(table): # investment
16 print('存储到mongodb成功')
17 sleep = np.random.randint(3, 7)
18 time.sleep(sleep)
19 except Exception:
20 print('存储到mongodb失败')
21 def spider_itjuzi(self, start_page, end_page):
22 for page in range(start_page, end_page):
23 print('下载第%s页:' % (page))
24 self.get_table(page)
25 print('下载完成')
26
27if __name__ == '__main__':
28 spider = ITjuzi()
29 spider.spider_itjuzi(1, 2)
这里,安装好 MongoingDB 数据库、Robo 3T 和 pymongo 库后,我们就可以开始使用了。
首先,对数据库进行初始化,然后指定(如果没有则生成)数据将要存放的数据库和集合名称。接着,定义了save_to_mongo 函数。由于表格里面的数据存储在键为 rows 的 value 值中,可使用 response['data']['rows']
来获取到 json 里面的嵌套数据,然后转为 DataFrame。DataFrame 存储 MongoDB 参考了 stackoverflow 上面的一个答案:json.loads(df.T.to_json()).values()。
然后,使用 mongo_collection1.insert_many(table) 方法将数据插入到 mongo_collection1,也就是 itjuzi_investevent 集合中。爬取完一页数据后,设置随机延时 3-6 s,避免爬取太频繁,这也能起到一定的反爬作用。
最后,我们定义一个分页循环爬取函数 spider_itjuzi,利用 for 循环设置爬取起始页数就可以了,爬取结果如下:
打开 Robo 3T,可以看到数据成功存储到 MongoDB 中了:
好,以上,我们就基本上完成了 2018 年投融资信息数据表的爬取,如果你想爬其他年份或者更多页的数据,更改相应的参数即可。
3.5. 导出到 csv
数据存好后,如果还不太熟悉 MongoDB 的对数据的操作,那么我们可以将数据导出为 csv,在 excel 中操作。MongoDB不能直接导出 csv,但操作起来也不麻烦,利用mongoexport
命令,几行代码就可以输出 csv。
mongoexport
导出 csv 的方法:
https://docs.mongodb.com/manual/reference/program/mongoexport/#mongoexport-fields-example
首先,运行 cmd,切换路径到 MongoDB 安装文件夹中的 bin 目录下,我这里是:
1cd C:\Program Files\MongoDB\Server\4.0\bin
接着,在桌面新建一个txt文件,命名为fields
,在里面输入我们需要输出的表格列名,如下所示:
然后,利用mongoexport
命令,按照:表格所在的数据库、集合、输出格式、导出列名文件位置和输出文件名的格式,编写好命令并运行就可以导出了:
1mongoexport --db ITjuzi --collection itjuzi_investevent --type=csv --fieldFile C:\Users\sony\Desktop\fields.txt --out C:\Users\sony\Desktop\investevent.csv
cmd 命令:
导出 csv 结果如下:
Tips:直接用 excel 打开可能会是乱码,需先用 Notepad++ 转换为 UTF-8 编码,然后 excel 再打开就正常了。
以上,我们就完成了整个数据表的爬取。
3.6. 完整代码
下面,可以再尝试爬取创业公司、投资机构和千里马的数据。他们的数据结构形式是一样的,只需要更换相应的参数就可以了,感兴趣的话可以尝试下。将上面的代码再稍微整理一下,完整代码如下:
1import requests
2import re
3import pymongo
4import random
5import time
6import json
7import random
8import numpy as np
9import csv
10import pandas as pd
11from fake_useragent import UserAgent
12import socket # 断线重试
13from urllib.parse import urlencode
14# 随机ua
15ua = UserAgent()
16# mongodb数据库初始化
17client = pymongo.MongoClient('localhost', 27017)
18# 获得数据库
19db = client.ITjuzi
20# 获得集合
21mongo_collection1 = db.itjuzi_investevent
22mongo_collection2 = db.itjuzi_company
23mongo_collection3 = db.itjuzi_investment
24mongo_collection4 = db.itjuzi_horse
25
26class itjuzi(object):
27 def __init__(self):
28
29 self.headers = {
30 'User-Agent': ua.random,
31 'X-Requested-With': 'XMLHttpRequest',
32 # 主页cookie
33 'Cookie': '你的cookie',
34 }
35 self.url = 'http://radar.itjuzi.com/investevent/info?' # investevent
36 # self.url = 'http://radar.itjuzi.com/company/infonew?' # company
37 # self.url = 'http://radar.itjuzi.com/investment/info?' # investment
38 # self.url = 'https://www.itjuzi.com/horse' # horse
39 self.session = requests.Session()
40
41 def get_table(self, page):
42 """
43 1 获取投融资事件网页内容
44 """
45 params = { # 1 invsestevent
46 'location': 'in',
47 'orderby': 'def',
48 'page': page,
49 'date':2018 #年份
50 }
51
52 # # # # # # # # # # # # # # # # # # # # # # # # # ## # # # # # # # # # # #
53 # params = { # 2 company
54 # 'page': page,
55 # # 'scope[]': 1, # 行业 1教育
56 # 'orderby': 'pv',
57 # 'born_year[]': 2018, # 只能单年,不能多年筛选,会保留最后一个
58 # }
59 # # # # # # # # # # # # # # # # # # # # # # # # # ## # # # # # # # # # # #
60 # params = { # 3 investment
61 # 'orderby': 'num',
62 # 'page': page
63 # }
64 # # # # # # # # # # # # # # # # # # # # # # # # # ## # # # # # # # # # # #
65 # params = { # 4 horse
66 # }
67 # 可能会遇到请求失败,则设置3次重新请求
68 retrytimes = 3
69 while retrytimes:
70 try:
71 response = self.session.get(
72 self.url, params=params, headers=self.headers,timeout = (5,20)).json()
73 self.save_to_mongo(response)
74 break
75 except socket.timeout:
76 print('下载第{}页,第{}次网页请求超时' .format(page,retrytimes))
77 retrytimes -=1
78
79 def save_to_mongo(self, response):
80 try:
81 data = response['data']['rows'] # dict可以连续选取字典层内的内容
82 # data =response # 爬取千里马时需替换为此data
83 df = pd.DataFrame(data)
84 table = json.loads(df.T.to_json()).values()
85 if mongo_collection1.insert_many(table): # investevent
86 # if mongo_collection2.insert_many(table): # company
87 # if mongo_collection3.insert_many(table): # investment
88 # if mongo_collection4.insert_many(table): # horse
89 print('存储到mongodb成功')
90 sleep = np.random.randint(3, 7)
91 time.sleep(sleep)
92 except Exception:
93 print('存储到mongodb失败')
94
95 def spider_itjuzi(self, start_page, end_page):
96 for page in range(start_page, end_page):
97 print('下载第%s页:' % (page))
98 self.get_table(page)
99 print('下载完成')
100
101if __name__ == '__main__':
102 spider = itjuzi()
103 spider.spider_itjuzi(1, 2)
源代码也可以在下面的链接中获取:
https://github.com/makcyun/web_scraping_with_python
4. 总结:
本文以桔子网为例,介绍了需登录网站的爬取方法。即:先模拟登录再爬取数据信息。但是还有一些网站登录时需要输入验证码,这让爬取难度又增加,后期会再进行介绍。
该网站相比之前的爬虫网站,反爬措施高了很多。本文通过设置随机延时、随机 UserAgent,可一定程度上增加爬虫的稳定性。但是仍然会受到反爬措施的限制,后期可尝试通过设置 IP 代理池进一步提升爬虫效率。
上面的爬虫程序在爬取过程容易中断,接着再进行爬取即可。但是手动修改非常不方便,也容易造成数据重复爬取或者漏爬。所以,为了更完整地爬取,需增加断点续传的功能。
补充一下,后期可能会在我的博客中不断更新、补充本文的内容。如想获得更多,可点击阅读原文,或者浏览器打开我的博客链接。
https://www.makcyun.top
本文完。